﻿using Prism.DryIoc;
using Prism.Ioc;
using Prism.Services.Dialogs;
using System;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using XrayDirection.Common;
using XrayDirection.Dialogs;
using XrayDirection.HelperManager;
using XrayDirection.Services;
using XrayDirection.ViewModels;
using XrayDirection.Views;

namespace XrayDirection
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }

        private void ShowSplashScren()
        {
            var splashScreen = new SplashScreen("Assets/Images/startFlash.png");
            // 是否自动关闭
            splashScreen.Show(true);
        }

        /*
         * 依赖注入的服务注册,其中服务注册的方式共有以下很多种:
         * 扩展方法,可以链式调用,因为返回的还是containerRegistry对象
         * 1)  public static IContainerRegistry RegisterInstance<TInterface>(this IContainerRegistry containerRegistry, TInterface instance)
         * 2) public static IContainerRegistry RegisterInstance<TInterface>(this IContainerRegistry containerRegistry, TInterface instance, string name)
         * 3) public static IContainerRegistry RegisterSingleton<TFrom, TTo>(this IContainerRegistry containerRegistry) 
         * 4) public static IContainerRegistry RegisterSingleton(this IContainerRegistry containerRegistry, Type type)
         * 5) public static IContainerRegistry RegisterSingleton<TFrom, TTo>(this IContainerRegistry containerRegistry)
         * 6) public static IContainerRegistry RegisterSingleton<T>(this IContainerRegistry containerRegistry)
         * 7) public static IContainerRegistry RegisterSingleton<T>(this IContainerRegistry containerRegistry, Func<object> factoryMethod)
         * 8) public static IContainerRegistry RegisterSingleton<T>(this IContainerRegistry containerRegistry, Func<IContainerProvider, object> factoryMethod)
         * 9) public static IContainerRegistry RegisterManySingleton<T>(this IContainerRegistry containerRegistry, params Type[] serviceTypes)
         * 10) public static IContainerRegistry Register(this IContainerRegistry containerRegistry, Type type)
         * 11)  public static IContainerRegistry Register<T>(this IContainerRegistry containerRegistry)
         * 12) public static IContainerRegistry Register(this IContainerRegistry containerRegistry, Type type, string name)
         * 13) public static IContainerRegistry Register<T>(this IContainerRegistry containerRegistry, string name)
         * 14) public static IContainerRegistry Register<TFrom, TTo>(this IContainerRegistry containerRegistry)
         * 15) public static IContainerRegistry Register<TFrom, TTo>(this IContainerRegistry containerRegistry, string name) 
         * 16) public static IContainerRegistry Register<T>(this IContainerRegistry containerRegistry, Func<object> factoryMethod)
         * 17) public static IContainerRegistry Register<T>(this IContainerRegistry containerRegistry, Func<IContainerProvider, object> factoryMethod)
         * 18) public static IContainerRegistry RegisterMany<T>(this IContainerRegistry containerRegistry, params Type[] serviceTypes)
         * 19) public static IContainerRegistry RegisterScoped(this IContainerRegistry containerRegistry, Type type)
         * 20) public static IContainerRegistry RegisterScoped<T>(this IContainerRegistry containerRegistry)
         * 21) public static IContainerRegistry RegisterScoped<TFrom, TTo>(this IContainerRegistry containerRegistry)
         * 22) public static IContainerRegistry RegisterScoped<T>(this IContainerRegistry containerRegistry, Func<object> factoryMethod)
         * 23) public static IContainerRegistry RegisterScoped<T>(this IContainerRegistry containerRegistry, Func<IContainerProvider, object> factoryMethod)
         * 24) public static bool IsRegistered<T>(this IContainerRegistry containerRegistry)
         * 25) public static bool IsRegistered<T>(this IContainerRegistry containerRegistry, string name)
         * 26) public static void RegisterDialog<TView>(this IContainerRegistry containerRegistry, string name = null)
         * 27) public static void RegisterDialog<TView, TViewModel>(this IContainerRegistry containerRegistry, string name = null)
         * 28) public static void RegisterDialogWindow<TWindow>(this IContainerRegistry containerRegistry)
         * 29) public static void RegisterDialogWindow<TWindow>(this IContainerRegistry containerRegistry, string name)
         * 30) public static void RegisterForNavigation(this IContainerRegistry containerRegistry, Type type, string name
         * 31)  public static void RegisterForNavigation<T>(this IContainerRegistry containerRegistry, string name = null)
         * 32) public static void RegisterForNavigation<TView, TViewModel>(this IContainerRegistry containerRegistry, string name = null)
         * 33) private static void RegisterForNavigationWithViewModel<TViewModel>(this IContainerRegistry containerRegistry, Type viewType, string name)
         */
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // 依赖注入注册服务
            containerRegistry.RegisterSingleton<MainViewModel>();
            containerRegistry.RegisterSingleton<ErrorShowViewModel>();

            containerRegistry.RegisterForNavigation<MainView, MainViewModel>();
            containerRegistry.RegisterSingleton<IClipboardService, ClipboardService>();

            containerRegistry.RegisterSingleton<SettingsViewModel>();
            containerRegistry.RegisterForNavigation<SettingsView, SettingsViewModel>();

            containerRegistry.RegisterSingleton<IndexViewModel>();
            containerRegistry.RegisterForNavigation<IndexView, IndexViewModel>();

            containerRegistry.RegisterSingleton<EapViewModel>();
            containerRegistry.RegisterForNavigation<EapView, EapViewModel>();

            containerRegistry.RegisterForNavigation<ToolView, IndexViewModel>();

            containerRegistry.RegisterSingleton<InputNextRecipeViewModel>();
            containerRegistry.RegisterSingleton<ChangeNextRecipeViewModel>();
            containerRegistry.RegisterSingleton<EditRecipeViewModel>();
            // 在导航的时候才会触发View和ViewModel的绑定,但是这里弹出的是要给窗口,所以这里设置其View和ViewModel的关联是不行的
            // 需要在服务那里手动的去关联
            //containerRegistry.RegisterForNavigation<InputNextRecipeView, InputNextRecipeViewModel>();

            containerRegistry.RegisterSingleton<IWindowService, WindowService>();
            containerRegistry.RegisterSingleton<IFileService, FileService>();

            containerRegistry.RegisterDialog<ErrorShowDialog, ErrorShowViewModel>();
            containerRegistry.RegisterDialog<ErrorDetailDialog, ErrorShowViewModel>();

            // 主窗口设置为单例模式
            containerRegistry.RegisterSingleton<MainView>();

            // 设置IcontainerProvider,方便依赖注入获取注册的对象
            ServiceProvider.SetContainerProvider(Container.Resolve<IContainerProvider>());
            // 设置IcontainerProvider,方便依赖注入获取注册的对象
            RequestHelper.SetContainerProvider(Container.Resolve<IContainerProvider>());
        }

        /// <summary>
        /// UI线程上发生未处理的异常,就会触发DispatcherUnhandledException事件,然后执行这个函数,这个事件在app.xaml中绑定
        /// 解释一下WPF各个线程的含义:
        /// 1) UI线程
        /// 在WPF中UI线程就是主线程,这个线程用来处理所有UI操作的,包括绘制界面,处理用户输入等等所有的UI元素
        /// 必须在UI线程上创建和访问
        /// 
        /// 2) 主线程
        /// 在大多数的应用程序中,主线程就是程序启动后创建的第一个线程,在WPF中,主线程就是UI线程
        /// 
        /// 3) 后台线程
        /// 后台线程就是一个在后台运行的线程,当所有的前台线程都结束的时候,后台线程也会结束.在.Net中你可以
        /// 设置Thread.IsBackground=True设置线程为后台线程
        /// 
        /// 4) 前台线程:
        /// 前台现场就是在前台运行的线程,前台线程可以阻止应用程序的退出. .Net中默认创建的线程都是前台线程.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void App_OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs exceptionArgs)
        {
            string exceptionLogDir = Path.Combine(ServiceProvider.LogDir!, "Crashes");
            if (!Directory.Exists(exceptionLogDir))
            {
                Directory.CreateDirectory(exceptionLogDir);
                FileDirHelper.ChangeDirAccessEveryone(exceptionLogDir);
            }

            // 保存异常信息到日志文件 
            File.WriteAllText(Path.Combine(exceptionLogDir, $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt"), exceptionArgs.Exception.ToString());
            // Handled=true, 表示异常已经处理过,系统不会再处理,这样可以避免应用程序的崩溃.
            exceptionArgs.Handled = true;
            // 弹窗提示
            var dialogService = Container.Resolve<IDialogService>();
            DialogParameters parameters = new DialogParameters()
            {
                { "Exception",exceptionArgs.Exception },
                { "Message",exceptionArgs.Exception.Message }
            };
            dialogService.ShowDialog(nameof(ErrorShowDialog), parameters, r => { });
            //new ErrorShowDialog(exceptionArgs.Exception, exceptionArgs.Exception.Message).ShowDialog();
        }

        // 程序启动的时候调用的方法.
        private void Application_OnStartUp(object sender, StartupEventArgs e)
        {
            // 1. 检测程序是否唯一性以及开启程序启动画面
            SingleInstanceManager.SingleInstanceCheck();
            // 启动画面必须是在单应用检测之后手动创建
            ShowSplashScren();
            WpfHelper wpfHelper = new WpfHelper();
            wpfHelper.GcTaskStart(500);

            // 2. 注册当前程序的非UI线程的异常处理函数
            AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException;

            // 3. 模拟一个异常(非UI异常)
            //Thread thread = new Thread(() =>
            //{
            //    try
            //    {
            //        throw new ArgumentOutOfRangeException("内部异常");
            //    }
            //    catch (Exception ex)
            //    {
            //        ex.Data.Add("DatabaseName", "数据库Fioman");
            //        ex.Data.Add("HappenTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            //        ex.HelpLink = "Https//www.baidu.com";
            //        throw new InvalidOperationException("外部异常", ex);
            //    }
            //});
            //thread.Start();
        }

        /// <summary>
        /// 这个事件是.NET框架级别的,会捕获所有在应用程序域中没有被处理的异常.这对于一般的.Net应用程序都适用.
        /// 这个事件只能用来记录异常或者是做一些清理工作,因为当事件被触发的时候,应用程序已经处于崩溃的状态,无法恢复.
        /// Application.Current.DispatcherUnhandledException这个事件是WPF应用程序级别的,只会捕获到在主UI
        /// 线程上的未补货单额异常,这个事件被触发的时候,异常还没有被.NET框架捕获,所以在这个事件处理器中处理异常并
        /// 防止应用程序崩溃.如果你将这个DispatcherUnhandledExceptionArgs的Hanlder属性设置为true,那么这个异常
        /// 就不会传播到OnCurrentDomainUnhandledExcepiton这个异常处理事件中.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            string exceptionDir = Path.Combine(ServiceProvider.LogDir!, "Crashes");
            if (!Directory.Exists(exceptionDir))
            {
                Directory.CreateDirectory(exceptionDir);
                FileDirHelper.ChangeDirAccessEveryone(exceptionDir);
            }

            File.WriteAllText(Path.Combine(exceptionDir, $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt"), e.ExceptionObject.ToString());
            // 弹窗提示,注意种类要用UI线程去弹窗
            if (e.ExceptionObject is Exception exception)
            {
                Current.Dispatcher.Invoke(() =>
                {
                    var dialogService = Container.Resolve<IDialogService>();
                    DialogParameters parameters = new DialogParameters()
                    {
                        { "Exception",exception},
                        { "Message",exception.Message}
                    };
                    dialogService.ShowDialog(nameof(ErrorShowDialog), parameters, result => { });
                });
            }
            // 然后关闭当前应用
            Current.Shutdown();
            Environment.Exit(0);
        }
    }
}
